package services

import (
    "context"
    "errors"
    "strings"
    "time"

    "github.com/google/uuid"
    "github.com/redis/go-redis/v9"
    "golang.org/x/crypto/bcrypt"

    "github.com/samaa/photosalesplus/backend/internal/config"
    "github.com/samaa/photosalesplus/backend/internal/models"
    "github.com/samaa/photosalesplus/backend/internal/repositories"
)

var ErrMerchantNotFound = errors.New("商户不存在")
var ErrMerchantPassword = errors.New("商户密码错误")

const merchantTokenPrefix = "auth:merchant_tokens:"

type MerchantAuthService interface {
    Login(ctx context.Context, username, password string) (string, *models.Merchant, error)
    Validate(ctx context.Context, token string) (uint, bool)
    Logout(ctx context.Context, token string) error
    EnsureDefault(ctx context.Context) error
}

type merchantAuthService struct {
    cfg   *config.Config
    redis *redis.Client
    repo  repositories.MerchantRepository
}

func NewMerchantAuthService(cfg *config.Config, r *redis.Client, repo repositories.MerchantRepository) MerchantAuthService {
    return &merchantAuthService{cfg: cfg, redis: r, repo: repo}
}

func (s *merchantAuthService) Login(ctx context.Context, username, password string) (string, *models.Merchant, error) {
    username = strings.TrimSpace(username)
    m, err := s.repo.FindByUsername(ctx, username)
    if err != nil || m == nil {
        return "", nil, ErrMerchantNotFound
    }
    if bcrypt.CompareHashAndPassword([]byte(m.PasswordHash), []byte(password)) != nil {
        return "", nil, ErrMerchantPassword
    }
    token := uuid.NewString()
    key := merchantTokenPrefix + token
    if err := s.redis.Set(ctx, key, m.ID, 24*time.Hour).Err(); err != nil {
        return "", nil, err
    }
    return token, m, nil
}

func (s *merchantAuthService) Validate(ctx context.Context, token string) (uint, bool) {
    if token == "" { return 0, false }
    val, err := s.redis.Get(ctx, merchantTokenPrefix+token).Uint64()
    if err != nil { return 0, false }
    return uint(val), true
}

func (s *merchantAuthService) Logout(ctx context.Context, token string) error {
    if token == "" { return nil }
    return s.redis.Del(ctx, merchantTokenPrefix+token).Err()
}

func (s *merchantAuthService) EnsureDefault(ctx context.Context) error {
    u := strings.TrimSpace(s.cfg.AuthUsername)
    p := strings.TrimSpace(s.cfg.AuthPassword)
    if u == "" || p == "" { return nil }
    if existing, _ := s.repo.FindByUsername(ctx, u); existing != nil { return nil }
    hash, err := bcrypt.GenerateFromPassword([]byte(p), bcrypt.DefaultCost)
    if err != nil { return err }
    m := &models.Merchant{Username: u, DisplayName: "默认商户", PasswordHash: string(hash)}
    return s.repo.Create(ctx, m)
}

